在軟體開發中,有時我們需要確保某個類別在整個應用程式中只存在一個實例。例如,當我們開發一個設定管理器時,需要保證在整個程式執行期間,設定的讀取和修改都要經由同一個物件。這時候單例模式 Singleton Pattern 就派上用場了。
單例模式是一種建立型設計模式,它確保一個類別在整個程式中只有一個實例,並且提供一個全域性都能存取的方式。這個模式通常用於管理共享資源、設定管理、日誌系統等需要單一實例的情況。聽起來很簡單,但是它的應用卻無處不在。
讓我們透過一個設定管理器的範例來看看如何在 C++ 中實作單例模式,什麼餓漢與懶漢的今天不多做介紹,我就介紹一個多數情況馬上就能使用的方式,最簡單好用也是常見的實作方式。
首先我們定義一個 ConfigManager
類別,它負責管理應用程式中的設定。
class ConfigManager {
public:
// 取得唯一實例的靜態方法
static ConfigManager& getInstance() {
static ConfigManager instance; // 唯一實例
return instance;
}
// 禁止複製建構與賦值
ConfigManager(const ConfigManager&) = delete;
void operator=(const ConfigManager&) = delete;
void setConfig(const std::string& key, const std::string& value) {
configData[key] = value;
}
std::string getConfig(const std::string& key) const {
if (configData.find(key) != configData.end()) {
return configData.at(key);
} else {
return "Config not found!";
}
}
private:
ConfigManager() {} // 禁止外部建立實例
std::unordered_map<std::string, std::string> configData; // 儲存設定的容器
};
在這個範例中,getInstance 方法使用 static 關鍵字保證了 ConfigManager 類別只有一個實例被建立。建構子是 private 的,這樣就無法透過 new 或其他方式在外部建立新的實例。另外也禁用了複製建構子和賦值運算子,避免無意間複製實例。
假設我們在一個應用程式中需要管理一些全域性的設定,例如資料庫連線字串、連接埠等。我們可以使用 ConfigManager
來統一管理這些設定。
int main() {
// 取得唯一的 ConfigManager 實例
ConfigManager& config = ConfigManager::getInstance();
std::cout << "Database host: " << config.getConfig("db_host") << std::endl;
config.setConfig("db_host", "localhost");
config.setConfig("db_port", "3306");
std::cout << "Database host: " << config.getConfig("db_host") << std::endl;
std::cout << "Database port: " << config.getConfig("db_port") << std::endl;
// 嘗試在另一個地方取得 ConfigManager 實例
ConfigManager& anotherConfig = ConfigManager::getInstance();
std::cout << "Database host: " << anotherConfig.getConfig("db_host") << std::endl;
return 0;
}
執行上述程式碼,我們會得到以下輸出:
Database host: Config not found!
Database host: localhost
Database port: 3306
Database host: localhost
這個範例中,我們先透過 getInstance()
取得唯一的 ConfigManager
實例,然後設定和取得不同的設定項目。無論程式的其他部分如何操作,都保證了設定管理器只有一個實例在執行。
以上我們介紹的是 Scott Meyers 在《Effective C++》中提出了一種簡潔的單例寫法,利用 local static object 在函式第一次被呼叫使用才初始化的特性,這寫法保證 C++11 以後是保證 thread-safe 的,另外也可以使用 STL 標準函式庫中的 std::call_once
來解決多執行緒建立單例這個問題,這邊不多贅述。
單例模式的優點如下:
單例模式的缺點如下:
單例模式在需要唯一實例的情境中是一種有效的設計模式,尤其是在設定管理器、日誌記錄器、資料庫連線、資源管理器等方面。透過本篇中的設定管理器範例,我們看到了如何在 C++ 中實作單例模式,以及這種模式的優缺點。在實際開發中,應該根據需求謹慎選擇是否使用單例模式,以平衡程式的設計和可維護性。
更多C++語言相關的文章,歡迎追蹤我的部落格,內有餓漢與懶漢的詳細介紹篇幅。
https://shengyu7697.github.io/cpp-singleton-pattern/